package com.yerbamateprimer.action.user.impl;

import java.io.Serializable;

import org.apache.commons.lang.builder.ReflectionToStringBuilder;
import org.apache.commons.lang.builder.ToStringStyle;

import com.google.common.base.Preconditions;
import com.yerbamateprimer.action.user.SynchronousUserAction;
import com.yerbamateprimer.advice.impl.RoundPausedAdvice;
import com.yerbamateprimer.model.User;
import com.yerbamateprimer.round.Round;
import com.yerbamateprimer.round.state.State;
import com.yerbamateprimer.round.state.StateName;
import com.yerbamateprimer.round.state.exit.StateExit;
import com.yerbamateprimer.round.state.exit.StateExitCause;
import com.yerbamateprimer.service.ClientService;
import com.yerbamateprimer.service.RoundService;

/**
 * This action is useful to
 * pause the yerba mate round.
 * 
 * @author Matias Liwski
 * @version 1.0
 */
public class PauseRoundUserAction implements SynchronousUserAction, Serializable {
	private static final long serialVersionUID = 2290858351309113183L;
	private User user;
	private transient RoundService roundService;
	private transient ClientService clientService;

	/**
	 * Default constructor, used by the serializer.
	 */
	public PauseRoundUserAction() {
	}

	/**
	 * @param user The user that want to execute this action.
	 */
	public PauseRoundUserAction(User user) {
		setUser(user);
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#execute()
	 */
	@Override
	public void execute() {
		checkArgumentPreconditions();
		Round round = this.roundService.getRoundForUser(this.user);
		Preconditions.checkState(round != null, "The user: " + this.user + " isn't participating at any round.");
		State currentState = round.getCurrentState();
		User primer = round.getPrimer();
		Preconditions.checkState(this.user.equals(primer), "Only the primer could pause a round ( primer: " + primer + ", user: " + this.user + ").");
		Preconditions.checkState(!StateName.PAUSE.equals(currentState.getName()), "The round is already paused (round: " + round + ").");
		
		setCurrentStateAsNext(round);
		round.setLastIntendedAction(this);
		this.clientService.send(new RoundPausedAdvice(round.getDrinkers()));
		currentState.getOut(round);
	}

	/**
	 * Check the preconditions of the arguments needed to execute this action.
	 */
	private void checkArgumentPreconditions() {
		Preconditions.checkArgument(this.user != null, "This action need a user to execute it.");
		Preconditions.checkArgument(this.roundService != null, "This action need a round service to execute it.");
		Preconditions.checkArgument(this.clientService != null, "This action needs a client service to execute it.");
	}

	/**
	 * Set the current state as the next state when get out of pause.
	 */
	private void setCurrentStateAsNext(Round round) {
		State currentState = round.getCurrentState();
		State pauseState = round.getState(StateName.PAUSE);
		StateExit pauseDoneStateExit = pauseState.getExit(StateExitCause.PAUSE_DONE);
		pauseDoneStateExit.setNextStateName(currentState.getName());
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#setUser(com.yerbamateprimer.model.User)
	 */
	@Override
	public void setUser(User user) {
		Preconditions.checkArgument(user != null, "This action needs a user to execute it.");
		this.user = user;
	}

	/* (non-Javadoc)
	 * @see com.yerbamateprimer.action.user.UserAction#getUser()
	 */
	@Override
	public User getUser() {
		return this.user;
	}

	/**
	 * @param roundService The round service of the Action, to set.
	 */
	public void setRoundService(RoundService roundService) {
		Preconditions.checkArgument(roundService != null, "This action needs a round service to execute it.");
		this.roundService = roundService;
	}

	/**
	 * @return The roundService of the Action.
	 */
	public RoundService getRoundService() {
		return this.roundService;
	}

	/**
	 * @param clientService The client service of the Action, to set.
	 */
	public void setClientService(ClientService clientService) {
		Preconditions.checkArgument(clientService != null, "This action needs a client service to execute it.");
		this.clientService = clientService;
	}

	/**
	 * @return The client service of the Action.
	 */
	public ClientService getClientService() {
		return this.clientService;
	}

	/* (non-Javadoc)
	 * @see java.lang.Object#toString()
	 */
	@Override
	public String toString() {
		return ReflectionToStringBuilder.toString(this, ToStringStyle.SHORT_PREFIX_STYLE, false, true);
	}
}